// import * as d3 from "../lib/d3.js";
import * as d3 from "d3";
import {
  clearContext
} from "../canvas/twod.js";
import {
  contrastRatio
} from "./color.js";
import {
  clamp,
  rescale
} from "../util/math.js";
import {
  handleAsChord
} from "./keyboard.js";
import {
  $
} from "../util/seq.js";
import {
  whenEnabled
} from "./util.js";
import {
  activeUnits
} from "./spotlight.js";
import {
  coalesceNum
} from "../util/values.js";

const DARK_TEXT_COLOR = d3.rgb("#444444");
const LIGHT_TEXT_COLOR = d3.rgb("#eeeeee");

function isDarkTextPreferred(c) {
  return contrastRatio(c, DARK_TEXT_COLOR) > contrastRatio(c, LIGHT_TEXT_COLOR);
}

/**
 * @param {number[]} colors
 * @param {number} pct
 * @returns d3 color object
 */
function colorAtPct(colors, pct) {
  const n = colors.length / 4;
  const j = clamp(Math.round(rescale(pct, 0, 100, 0, n - 1)), 0, n - 1) * 4;
  return d3.rgb(colors[j], colors[j + 1], colors[j + 2], colors[j + 3] / 255);
}

/**
 * @param canvas
 * @param {number[]} mouse
 * @returns {number}
 */
function mouseToPercent(canvas, mouse) {
  const {
    clientWidth,
    clientHeight
  } = canvas.node();
  if (clientHeight < clientWidth) {
    const x = coalesceNum(mouse?. [0], 0);
    const [lo, hi] = canvas.style("direction") === "rtl" ? [100, 0] : [0, 100];
    return clamp(rescale(x, 0, canvas.node().clientWidth - 1, lo, hi), 0, 100);
  } else {
    const y = coalesceNum(mouse?. [1], 0);
    const [lo, hi] = [0, 100];
    return clamp(rescale(y, 0, canvas.node().clientHeight - 1, lo, hi), 0, 100);

  }
}

/**
 * @param {HTMLCanvasElement} canvas
 * @param layerAgent
 */
function defineCanvasPainter(canvas, layerAgent) {

  function updateCanvas(layer) {
    const {
      clientWidth,
      width,
      clientHeight,
      height
    } = canvas;
    const ctx = canvas.getContext("2d");
    const colors = layer?.scale.colors ?? [];
    clearContext(ctx);
    if (colors.length === 0) {
      return;
    }
    if (clientWidth < clientHeight) {
      for (let i = 0; i < height; i++) {
        ctx.fillStyle = String(colorAtPct(colors, rescale(i, 0, height - 1, 0, 100)));
        ctx.fillRect(0, height - i, width, 1);
      }
    } else {
      for (let i = 0; i < width; i++) {
        ctx.fillStyle = String(colorAtPct(colors, rescale(i, 0, width - 1, 0, 100)));
        ctx.fillRect(i, 0, 1, height);
      }
    }
  }

  updateCanvas(layerAgent.value()); // initialize
  layerAgent.on($`update`, updateCanvas);
}

/**
 * @param canvas
 * @param cursor
 * @param {Handle} colorbar_cursor
 * @param {Function} showTooltip
 */
function defineColorbarCursorNav(canvas, cursor, colorbar_cursor, showTooltip) {

  const {
    clientWidth,
    clientHeight
  } = canvas.node();

  function updateDOM(value) {
    const pct = `calc(${clamp(coalesceNum(value, 0), 0, 100).toFixed(2)}% - 2px)`;
    if (clientHeight < clientWidth) {
      const [left, right] = canvas.style("direction") === "rtl" ? [null, pct] : [pct, null];
      cursor.style("left", left).style("right", right);
    } else {
      const [top, bottom] = [pct, null];
      cursor.style("top", top).style("bottom", bottom);
    }
  }

  updateDOM(colorbar_cursor.get()); // initialize
  colorbar_cursor.on($`change`, updateDOM);

  canvas.on($`click`, whenEnabled(function(event) {
    const val = mouseToPercent(canvas, d3.pointer(event));
    colorbar_cursor.set(val, {
      actor: this
    });
    cursor.node().focus();
  }));

  cursor.on($`keydown`, handleAsChord(chord => {
    const meta = {
      actor: this
    };

    function move(step) {
      const next = clamp(coalesceNum(colorbar_cursor.get(), 0) + step, 0, 100);
      colorbar_cursor.set(next, meta);
      showTooltip();
      return true;
    }

    const dir = canvas.style("direction") === "rtl" ? -1 : 1;
    switch (chord) {
      case "Home":
        return colorbar_cursor.set(0, meta), true;
      case "Shift-ArrowLeft":
        return move(dir * -0.1);
      case "Shift-ArrowRight":
        return move(dir * 0.1);
      case "Shift-ArrowUp":
        return move(0.1);
      case "Shift-ArrowDown":
        return move(-0.1);
      case "ArrowLeft":
        return move(-dir);
      case "ArrowRight":
        return move(dir);
      case "ArrowUp":
        return move(1);
      case "ArrowDown":
        return move(-1);
      case "PageUp":
        return move(10);
      case "PageDown":
        return move(-10);
      case "End":
        return colorbar_cursor.set(100, meta), true;
    }
  }));
}

/**
 * @param colorbar d3 selection of the single colorbar container
 * @param {Handle} colorbar_cursor
 * @param {Handle} selected_units
 * @param layerAgent
 * @param tooltipController
 */
export function defineColorbar(colorbar, colorbar_cursor, selected_units, layerAgent, tooltipController) {
  const canvas = colorbar.select("canvas");
  canvas.attr("width", 500).attr("height", 500);
  const cursor = colorbar.select("#colorbar-cursor");
  const tooltip = colorbar.select("#colorbar-tt");
  const showTooltip = () => tooltipController.showTooltip(tooltip);

  defineCanvasPainter(canvas.node(), layerAgent);
  defineColorbarCursorNav(canvas, cursor, colorbar_cursor, showTooltip);

  function updateTooltipBackground(pct) {
    const colors = layerAgent.value()?.scale.colors ?? [];
    const color = colors.length > 0 ? colorAtPct(colors, pct) : null;
    tooltip
      .style("background", color)
      .classed("dark-text", color !== null && isDarkTextPreferred(color));
  }

  function updateTooltipContent(pct) {
    const layer = layerAgent.value();
    const units = activeUnits(layer, selected_units.get());
    const val = layer?.valueInRange?.(pct / 100);
    const {
      scalarHTML = ""
    } = units?.format(val) ?? {};
    tooltip.html(scalarHTML);
  }

  function updateTooltipDOM(value) {
    const pct = 100-clamp(coalesceNum(value ?? colorbar_cursor.get(), 0), 0, 100);
    updateTooltipBackground(pct);
    updateTooltipContent(pct);
  }

  canvas.on($`mousemove`, event => {
    const val = mouseToPercent(canvas, d3.pointer(event))
    updateTooltipDOM(val);
    showTooltip();
  });

  const update = () => updateTooltipDOM();
  cursor.on($`focus`, update);
  colorbar_cursor.on($`change`, update);
  selected_units.on($`change`, update);
  layerAgent.on($`update`, update);
}
